// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcBezierRectangle2.h"
#include "MgcTriMesh.h"

MgcImplementRTTI(MgcBezierRectangle2,MgcBezierRectangle);
MgcImplementStream(MgcBezierRectangle2);

// macros for initialization in the subdivision
#define XA(i) akCtrlPoint[m_auiIndex[i]]
#define CA(i) akCtrlColor[m_auiIndex[i]]
#define TA(i) akCtrlTexture[m_auiIndex[i]]

//----------------------------------------------------------------------------
MgcBezierRectangle2::MgcBezierRectangle2 (unsigned int* auiIndex)
    :
    MgcBezierRectangle(2,9,auiIndex)
{
}
//----------------------------------------------------------------------------
MgcBezierRectangle2::MgcBezierRectangle2 ()
{
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::InitializePoints (const MgcVector3* akCtrlPoint,
    MgcVector3& rkXsstt, BlockParameters& rkBP)
{
    // Xss
    rkBP.m_akXss[0] = 2.0*(XA(0) - 2.0*XA(1) + XA(2));
    rkBP.m_akXss[1] = 2.0*(XA(6) - 2.0*XA(7) + XA(8));

    // Xtt
    rkBP.m_akXtt[0] = 2.0*(XA(0) - 2.0*XA(3) + XA(6));
    rkBP.m_akXtt[1] = 2.0*(XA(2) - 2.0*XA(5) + XA(8));

    // Xsstt
    rkXsstt = 4.0*(XA(0) + XA(2) + XA(6) + XA(8) - 2.0*(XA(1) + XA(3) + XA(5)
        + XA(7)) + 4.0*XA(4));
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::InitializeNormals (const MgcVector3* akCtrlPoint,
    BlockParameters& rkBP)
{
    // Xsst
    rkBP.m_akXsst[0] = 4.0*(XA(3)+XA(5)-XA(0)-XA(2)+2.0*(XA(1)-XA(4)));
    rkBP.m_akXsst[1] = 4.0*(XA(6)+XA(8)-XA(3)-XA(5)+2.0*(XA(4)-XA(7)));

    // Xstt
    rkBP.m_akXstt[0] = 4.0*(XA(1)+XA(7)-XA(0)-XA(6)+2.0*(XA(3)-XA(4)));
    rkBP.m_akXstt[1] = 4.0*(XA(2)+XA(8)-XA(1)-XA(7)+2.0*(XA(4)-XA(5)));
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::InitializeColors (const MgcColor* akCtrlColor,
    MgcColor& rkCsstt, BlockParameters& rkBP)
{
    // Css
    rkBP.m_akCss[0] = 2.0*(CA(0) - 2.0*CA(1) + CA(2));
    rkBP.m_akCss[1] = 2.0*(CA(6) - 2.0*CA(7) + CA(8));

    // Ctt
    rkBP.m_akCtt[0] = 2.0*(CA(0) - 2.0*CA(3) + CA(6));
    rkBP.m_akCtt[1] = 2.0*(CA(2) - 2.0*CA(5) + CA(8));

    // Csstt
    rkCsstt = 4.0*(CA(0) + CA(2) + CA(6) + CA(8) - 2.0*(CA(1) + CA(3) + CA(5)
        + CA(7)) + 4.0*CA(4));
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::InitializeTextures (const MgcVector2* akCtrlTexture,
    MgcVector2& rkTsstt, BlockParameters& rkBP)
{
    // Tss
    rkBP.m_akTss[0] = 2.0*(TA(0) - 2.0*TA(1) + TA(2));
    rkBP.m_akTss[1] = 2.0*(TA(6) - 2.0*TA(7) + TA(8));

    // Ttt
    rkBP.m_akTtt[0] = 2.0*(TA(0) - 2.0*TA(3) + TA(6));
    rkBP.m_akTtt[1] = 2.0*(TA(2) - 2.0*TA(5) + TA(8));

    // Tsstt
    rkTsstt = 4.0*(TA(0) + TA(2) + TA(6) + TA(8) - 2.0*(TA(1) + TA(3) + TA(5)
        + TA(7)) + 4.0*TA(4));
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::Tessellate (unsigned int uiLevel,
    const MgcVector3* akCtrlPoint, const MgcColor* akCtrlColor,
    const MgcVector2* akCtrlTexture, MgcTriMesh* pkMesh,
    unsigned int& ruiVertexStart, unsigned int& ruiTriangleStart)
{
    GenerateConnectivity(uiLevel,pkMesh,ruiTriangleStart);

    // number of vertices in the subdivision
    unsigned int uiQuantity = GetVerticesPerRectanglePatch(uiLevel);

    // indices of four corners of patch, I[s][t]
    unsigned int uiTwoPowL = (1 << uiLevel);
    BlockParameters kBP;
    kBP.m_uiI00 = 0;
    kBP.m_uiI01 = uiTwoPowL*(uiTwoPowL + 1);
    kBP.m_uiI10 = uiTwoPowL;
    kBP.m_uiI11 = kBP.m_uiI01 + uiTwoPowL;

    // vertices for subdivision
    MgcVector3* akX = pkMesh->Vertices() + ruiVertexStart;
    akX[kBP.m_uiI00] = XA(0);
    akX[kBP.m_uiI01] = XA(6);
    akX[kBP.m_uiI10] = XA(2);
    akX[kBP.m_uiI11] = XA(8);

    // derivatives for subdivision (for normal vectors)
    MgcVector3* akXs;
    MgcVector3* akXt;
    if ( pkMesh->Normals() )
    {
        akXs = new MgcVector3[uiQuantity];
        akXs[kBP.m_uiI00] = 2.0*(XA(1) - XA(0));
        akXs[kBP.m_uiI01] = 3.0*(XA(7) - XA(6));
        akXs[kBP.m_uiI10] = 3.0*(XA(2) - XA(1));
        akXs[kBP.m_uiI11] = 3.0*(XA(8) - XA(7));

        akXt = new MgcVector3[uiQuantity];
        akXt[kBP.m_uiI00] = 3.0*(XA(3) - XA(0));
        akXt[kBP.m_uiI01] = 3.0*(XA(6) - XA(3));
        akXt[kBP.m_uiI10] = 3.0*(XA(5) - XA(2));
        akXt[kBP.m_uiI11] = 3.0*(XA(8) - XA(5));
    }
    else
    {
        akXs = 0;
        akXt = 0;
    }

    // colors for subdivision
    MgcColor* akColor;
    if ( pkMesh->Colors() )
    {
        akColor = pkMesh->Colors() + ruiVertexStart;
        akColor[kBP.m_uiI00] = CA(0);
        akColor[kBP.m_uiI01] = CA(6);
        akColor[kBP.m_uiI10] = CA(2);
        akColor[kBP.m_uiI11] = CA(8);
    }
    else
    {
        akColor = 0;
    }

    // textures for subdivision
    MgcVector2* akTexture;
    if ( pkMesh->Textures() )
    {
        akTexture = pkMesh->Textures() + ruiVertexStart;
        akTexture[kBP.m_uiI00] = TA(0);
        akTexture[kBP.m_uiI01] = TA(6);
        akTexture[kBP.m_uiI10] = TA(2);
        akTexture[kBP.m_uiI11] = TA(8);
    }
    else
    {
        akTexture = 0;
    }

    // recursive subdivision
    if ( uiLevel > 0 )
    {
        MgcVector3 kXsstt;
        MgcColor kCsstt;
        MgcVector2 kTsstt;

        InitializePoints(akCtrlPoint,kXsstt,kBP);

        if ( akXs )
            InitializeNormals(akCtrlPoint,kBP);

        if ( akCtrlColor )
            InitializeColors(akCtrlColor,kCsstt,kBP);

        if ( akCtrlTexture )
            InitializeTextures(akCtrlTexture,kTsstt,kBP);

        Subdivide(--uiLevel,0.25,akX,akXs,akXt,akColor,akTexture,kXsstt,
            kCsstt,kTsstt,kBP);
    }

    // calculate unit-length normals from derivative vectors
    if ( pkMesh->Normals() )
    {
        MgcVector3* akNormal = pkMesh->Normals() + ruiVertexStart;
        for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
            akNormal[uiI] = akXs[uiI].UnitCross(akXt[uiI]);
        delete[] akXs;
        delete[] akXt;
    }

    ruiVertexStart += uiQuantity;
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::Subdivide (unsigned int uiLevel, MgcReal fDSqr,
    MgcVector3* akX, MgcVector3* akXs, MgcVector3* akXt, MgcColor* akColor,
    MgcVector2* akTexture, MgcVector3& rkXsstt, MgcColor& rkCsstt,
    MgcVector2& rkTsstt, BlockParameters& rkBP)
{
    // subdivision indices
    unsigned int uiIM0 = (rkBP.m_uiI00 + rkBP.m_uiI10) >> 1;
    unsigned int uiIM1 = (rkBP.m_uiI01 + rkBP.m_uiI11) >> 1;
    unsigned int uiI0M = (rkBP.m_uiI00 + rkBP.m_uiI01) >> 1;
    unsigned int uiI1M = (rkBP.m_uiI10 + rkBP.m_uiI11) >> 1;
    unsigned int uiIMM = (uiI0M + uiI1M) >> 1;

    // vertices

    // top and bottom s-edge subdivision
    MgcVector3 kXttM0 = 0.5*(rkBP.m_akXtt[0]+rkBP.m_akXtt[1]-fDSqr*rkXsstt);
    akX[uiIM0] = 0.5*(akX[rkBP.m_uiI00] + akX[rkBP.m_uiI10] -
        fDSqr*rkBP.m_akXss[0]);
    akX[uiIM1] = 0.5*(akX[rkBP.m_uiI01] + akX[rkBP.m_uiI11] -
        fDSqr*rkBP.m_akXss[1]);

    // left and right t-edge subdivision
    MgcVector3 kXss0M = 0.5*(rkBP.m_akXss[0]+rkBP.m_akXss[1]-fDSqr*rkXsstt);
    akX[uiI0M] = 0.5*(akX[rkBP.m_uiI00] + akX[rkBP.m_uiI01] -
        fDSqr*rkBP.m_akXtt[0]);
    akX[uiI1M] = 0.5*(akX[rkBP.m_uiI10] + akX[rkBP.m_uiI11] -
        fDSqr*rkBP.m_akXtt[1]);

    // center subdivision
    akX[uiIMM] = 0.5*(akX[uiI0M] + akX[uiI1M] - fDSqr*kXss0M);

    // derivatives (for normal vectors)
    MgcVector3 kXsst0M, kXsttM0;
    if ( akXs )
    {
        // top and bottom s-edge subdivision
        akXs[uiIM0] = 0.5*(akXs[rkBP.m_uiI00] + akXs[rkBP.m_uiI10]);
        akXt[uiIM0] = 0.5*(akXt[rkBP.m_uiI00] + akXt[rkBP.m_uiI10] -
            fDSqr*rkBP.m_akXsst[0]);
        akXs[uiIM1] = 0.5*(akXs[rkBP.m_uiI01] + akXs[rkBP.m_uiI11]);
        akXt[uiIM1] = 0.5*(akXt[rkBP.m_uiI01] + akXt[rkBP.m_uiI11] -
            fDSqr*rkBP.m_akXsst[1]);

        kXsttM0 = 0.5*(rkBP.m_akXstt[0] + rkBP.m_akXstt[1]);

        // left and right t-edge subdivision
        akXs[uiI0M] = 0.5*(akXs[rkBP.m_uiI00] + akXs[rkBP.m_uiI01] -
            fDSqr*rkBP.m_akXstt[0]);
        akXt[uiI0M] = 0.5*(akXt[rkBP.m_uiI00] + akXt[rkBP.m_uiI01]);
        akXs[uiI1M] = 0.5*(akXs[rkBP.m_uiI10] + akXs[rkBP.m_uiI11] -
            fDSqr*rkBP.m_akXstt[1]);
        akXt[uiI1M] = 0.5*(akXt[rkBP.m_uiI10] + akXt[rkBP.m_uiI11]);

        kXsst0M = 0.5*(rkBP.m_akXsst[0] + rkBP.m_akXsst[1]);

        // center subdivision
        akXs[uiIMM] = 0.5*(akXs[uiI0M] + akXs[uiI1M]);
        akXt[uiIMM] = 0.5*(akXt[uiIM0] + akXt[uiIM1]);
    }

    // colors
    MgcColor kCss0M, kCttM0;
    if ( akColor )
    {
        // top and bottom s-edge subdivision
        kCttM0 = 0.5*(rkBP.m_akCtt[0]+rkBP.m_akCtt[1]-fDSqr*rkCsstt);
        akColor[uiIM0] = 0.5*(akColor[rkBP.m_uiI00] + akColor[rkBP.m_uiI10] -
            fDSqr*rkBP.m_akCss[0]);
        akColor[uiIM1] = 0.5*(akColor[rkBP.m_uiI01] + akColor[rkBP.m_uiI11] -
            fDSqr*rkBP.m_akCss[1]);

        // left and right t-edge subdivision
        kCss0M = 0.5*(rkBP.m_akCss[0]+rkBP.m_akCss[1]-fDSqr*rkCsstt);
        akColor[uiI0M] = 0.5*(akColor[rkBP.m_uiI00] + akColor[rkBP.m_uiI01] -
            fDSqr*rkBP.m_akCtt[0]);
        akColor[uiI1M] = 0.5*(akColor[rkBP.m_uiI10] + akColor[rkBP.m_uiI11] -
            fDSqr*rkBP.m_akCtt[1]);

        // center subdivision
        akColor[uiIMM] = 0.5*(akColor[uiI0M] + akColor[uiI1M] - fDSqr*kCss0M);
    }

    // textures
    MgcVector2 kTss0M, kTttM0;
    if ( akTexture )
    {
        // top and bottom s-edge subdivision
        kTttM0 = 0.5*(rkBP.m_akTtt[0]+rkBP.m_akTtt[1]-fDSqr*rkTsstt);
        akTexture[uiIM0] = 0.5*(akTexture[rkBP.m_uiI00] +
            akTexture[rkBP.m_uiI10] - fDSqr*rkBP.m_akTss[0]);
        akTexture[uiIM1] = 0.5*(akTexture[rkBP.m_uiI01] +
            akTexture[rkBP.m_uiI11] - fDSqr*rkBP.m_akTss[1]);

        // left and right t-edge subdivision
        kTss0M = 0.5*(rkBP.m_akTss[0]+rkBP.m_akTss[1]-fDSqr*rkTsstt);
        akTexture[uiI0M] = 0.5*(akTexture[rkBP.m_uiI00] +
            akTexture[rkBP.m_uiI01] - fDSqr*rkBP.m_akTtt[0]);
        akTexture[uiI1M] = 0.5*(akTexture[rkBP.m_uiI10] +
            akTexture[rkBP.m_uiI11] - fDSqr*rkBP.m_akTtt[1]);

        // center subdivision
        akTexture[uiIMM] = 0.5*(akTexture[uiI0M] + akTexture[uiI1M] -
            fDSqr*kTss0M);
    }

    // recurse on four children
    if ( uiLevel > 0 )
    {
        uiLevel--;
        fDSqr *= 0.25;

        BlockParameters kSubBP;

        // subblock [s0,sM]x[t0,tM]
        kSubBP.m_uiI00 = rkBP.m_uiI00;
        kSubBP.m_uiI01 = uiI0M;
        kSubBP.m_uiI10 = uiIM0;
        kSubBP.m_uiI11 = uiIMM;

        kSubBP.m_akXss[0] = rkBP.m_akXss[0];
        kSubBP.m_akXss[1] = kXss0M;
        kSubBP.m_akXtt[0] = rkBP.m_akXtt[0];
        kSubBP.m_akXtt[1] = kXttM0;

        if ( akXs )
        {
            kSubBP.m_akXsst[0] = rkBP.m_akXsst[0];
            kSubBP.m_akXsst[1] = kXsst0M;
            kSubBP.m_akXstt[0] = rkBP.m_akXstt[0];
            kSubBP.m_akXstt[1] = kXsttM0;
        }

        if ( akColor )
        {
            kSubBP.m_akCss[0] = rkBP.m_akCss[0];
            kSubBP.m_akCss[1] = kCss0M;
            kSubBP.m_akCtt[0] = rkBP.m_akCtt[0];
            kSubBP.m_akCtt[1] = kCttM0;
        }

        if ( akTexture )
        {
            kSubBP.m_akTss[0] = rkBP.m_akTss[0];
            kSubBP.m_akTss[1] = kTss0M;
            kSubBP.m_akTtt[0] = rkBP.m_akTtt[0];
            kSubBP.m_akTtt[1] = kTttM0;
        }

        Subdivide(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,rkXsstt,
            rkCsstt,rkTsstt,kSubBP);

        // subblock [s0,sM]x[tM,t1]
        kSubBP.m_uiI00 = uiI0M;
        kSubBP.m_uiI01 = rkBP.m_uiI01;
        kSubBP.m_uiI10 = uiIMM;
        kSubBP.m_uiI11 = uiIM1;

        kSubBP.m_akXss[0] = kXss0M;
        kSubBP.m_akXss[1] = rkBP.m_akXss[1];
        kSubBP.m_akXtt[0] = rkBP.m_akXtt[0];
        kSubBP.m_akXtt[1] = kXttM0;

        if ( akXs )
        {
            kSubBP.m_akXsst[0] = kXsst0M;
            kSubBP.m_akXsst[1] = rkBP.m_akXsst[1];
            kSubBP.m_akXstt[0] = rkBP.m_akXstt[0];
            kSubBP.m_akXstt[1] = kXsttM0;
        }

        if ( akColor )
        {
            kSubBP.m_akCss[0] = kCss0M;
            kSubBP.m_akCss[1] = rkBP.m_akCss[1];
            kSubBP.m_akCtt[0] = rkBP.m_akCtt[0];
            kSubBP.m_akCtt[1] = kCttM0;
        }

        if ( akTexture )
        {
            kSubBP.m_akTss[0] = kTss0M;
            kSubBP.m_akTss[1] = rkBP.m_akTss[1];
            kSubBP.m_akTtt[0] = rkBP.m_akTtt[0];
            kSubBP.m_akTtt[1] = kTttM0;
        }

        Subdivide(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,rkXsstt,
            rkCsstt,rkTsstt,kSubBP);

        // subblock [sM,s1]x[t0,tM]
        kSubBP.m_uiI00 = uiIM0;
        kSubBP.m_uiI01 = uiIMM;
        kSubBP.m_uiI10 = rkBP.m_uiI10;
        kSubBP.m_uiI11 = uiI1M;

        kSubBP.m_akXss[0] = rkBP.m_akXss[0];
        kSubBP.m_akXss[1] = kXss0M;
        kSubBP.m_akXtt[0] = kXttM0;
        kSubBP.m_akXtt[1] = rkBP.m_akXtt[1];

        if ( akXs )
        {
            kSubBP.m_akXsst[0] = rkBP.m_akXsst[0];
            kSubBP.m_akXsst[1] = kXsst0M;
            kSubBP.m_akXstt[0] = kXsttM0;
            kSubBP.m_akXstt[1] = rkBP.m_akXstt[1];
        }

        if ( akColor )
        {
            kSubBP.m_akCss[0] = rkBP.m_akCss[0];
            kSubBP.m_akCss[1] = kCss0M;
            kSubBP.m_akCtt[0] = kCttM0;
            kSubBP.m_akCtt[1] = rkBP.m_akCtt[1];
        }

        if ( akTexture )
        {
            kSubBP.m_akTss[0] = rkBP.m_akTss[0];
            kSubBP.m_akTss[1] = kTss0M;
            kSubBP.m_akTtt[0] = kTttM0;
            kSubBP.m_akTtt[1] = rkBP.m_akTtt[1];
        }

        Subdivide(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,rkXsstt,
            rkCsstt,rkTsstt,kSubBP);

        // subblock [sM,s1]x[tM,t1]
        kSubBP.m_uiI00 = uiIMM;
        kSubBP.m_uiI01 = uiIM1;
        kSubBP.m_uiI10 = uiI1M;
        kSubBP.m_uiI11 = rkBP.m_uiI11;

        kSubBP.m_akXss[0] = kXss0M;
        kSubBP.m_akXss[1] = rkBP.m_akXss[1];
        kSubBP.m_akXtt[0] = kXttM0;
        kSubBP.m_akXtt[1] = rkBP.m_akXtt[1];

        if ( akXs )
        {
            kSubBP.m_akXsst[0] = kXsst0M;
            kSubBP.m_akXsst[1] = rkBP.m_akXsst[1];
            kSubBP.m_akXstt[0] = kXsttM0;
            kSubBP.m_akXstt[1] = rkBP.m_akXstt[1];
        }

        if ( akColor )
        {
            kSubBP.m_akCss[0] = kCss0M;
            kSubBP.m_akCss[1] = rkBP.m_akCss[1];
            kSubBP.m_akCtt[0] = kCttM0;
            kSubBP.m_akCtt[1] = rkBP.m_akCtt[1];
        }

        if ( akTexture )
        {
            kSubBP.m_akTss[0] = kTss0M;
            kSubBP.m_akTss[1] = rkBP.m_akTss[1];
            kSubBP.m_akTtt[0] = kTttM0;
            kSubBP.m_akTtt[1] = rkBP.m_akTtt[1];
        }

        Subdivide(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,rkXsstt,
            rkCsstt,rkTsstt,kSubBP);
    }
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// streaming
//----------------------------------------------------------------------------
MgcObject* MgcBezierRectangle2::Factory (MgcStream& rkStream)
{
    MgcBezierRectangle2* pkObject = new MgcBezierRectangle2;
    MgcStream::Link* pkLink = new MgcStream::Link(pkObject);
    pkObject->Load(rkStream,pkLink);
    return pkObject;
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierRectangle::Load(rkStream,pkLink);
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierRectangle::Link(rkStream,pkLink);
}
//----------------------------------------------------------------------------
bool MgcBezierRectangle2::Register (MgcStream& rkStream)
{
    return MgcBezierRectangle::Register(rkStream);
}
//----------------------------------------------------------------------------
void MgcBezierRectangle2::Save (MgcStream& rkStream)
{
    MgcBezierRectangle::Save(rkStream);
}
//----------------------------------------------------------------------------
